feat(core): session-scoped dock UI state — restore open/selected dock, tab, and scroll across reloads - #527
Conversation
…, tab, and scroll across reloads Splits the dock panel's persisted state in two: config (geometry/position/ mode/inactiveTimeout) stays in localStorage (vite-devtools-dock-state, cross-tab), while *state* (open, selected dock, active tab, scroll position) moves to sessionStorage (vite-devtools-dock-session, per-tab) — a full page reload no longer leaves the panel open-but-empty or resets scroll/tab. - createDocksContext (context.ts) now backs open + selectedId with a single useSessionStorage store, merged with the geometry-only panelStore into the same DockPanelStorage shape every dock component already reads/writes through context.panel.store — only open's backing store changes. - A restored selectedId is validated once at boot (once dock entries have actually loaded) against being a real selectable leaf — not a group, a subTabs anchor, or a stale id — clearing it without routing through switchEntry (which would force the panel back open). Past that one-time check, switchEntry may legitimately land on a group/anchor id itself (e.g. a subTabs anchor with no live member yet) — this doesn't re-litigate that. - DockEmbedded now mirrors DockStandalone's own auto-select-first-entry boot guard, gated on the panel actually being open. - ViewJsonRender provides its dock entry id for descendants to inject, and restores/persists its own scroll position per dock, debounced. - New useUncontrolledValue composable: session-persisted fallback for a json-render element's own uncontrolled value, keyed by the injected dock id plus a signature of the element's static shape (works around FlatElement.key not being forwarded to registry components). Tabs now uses it instead of its hand-rolled ref; Select/TextInput/Switch — until now inert while unbound — get it too. Two behavior changes ship with this, both intentional: - Opening the docks in one tab no longer auto-opens them in another tab of the same page (open moved from localStorage to sessionStorage). - The very first load after this ships, the panel starts closed even if a user's old vite-devtools-dock-state said open: true — that key's open is simply never read again. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
…ionStorage split Folds selectedId into the existing localStorage-backed panelStore instead of a separate sessionStorage-scoped store: it's a browser-local singleton exactly like open/mode/position, so it belongs in the same object and gets mirrored for free once that object is shared over RPC. Only the active spec tab and scroll position stay genuinely per-tab (still sessionStorage, via each ViewJsonRender's own independent key) since nothing server-side needs them and syncing them across tabs would be unwanted UX.
@vitejs/devtools
@vitejs/devtools-kit
@vitejs/devtools-oxc
@vitejs/devtools-rolldown
@vitejs/devtools-vite
@vitejs/devtools-vitest
commit: |
|
Thanks for this fix! As of #529, the client UI previously at Since this PR's changes target code that no longer lives here, we're labeling it |
|
Closing in favor of a consolidated issue on devframe, per the discussion on #525 — filed both the need and the two problems (panel-exit signal, UI state across reload) together rather than as two separate PRs to port: devframes/devframe#229 |
Why
Reloading the page (dev-server restart, an HMR full-reload, etc.) always dropped back to whatever dock is open by default, which gets annoying mid-debugging.
What changed
ViewJsonRenderdock —sessionStorage, keyed per dock and tab, since nothing server-side needs to see these and syncing them across tabs would be unwanted UX.selectedId, folded into the existing localStorage-backedpanelStorealongsideopen/mode/position/geometry, rather than a second store, since it's a browser-local singleton exactly like those already are.Linked Issues
Additional context
selectedIdliving in the same object asopenmeans it inherits #525's shared-state mirror for free, if that one lands — no separate wiring needed there.Demo
Screen.Recording.2026-08-10.at.15.38.35.mov